
#include <Windows.h>
#include "oglRenderer.h"

#include <renderer/camera.h>

USING_NAMESPACE_OXGE



void oglRenderer::Render( SceneNode* pNode ,const ViewPort& viewport )
{
	if(!pNode)
		return; 

	
	glViewport(viewport.m_x, viewport.m_y, viewport.m_width, viewport.m_height);

	glClearColor(0,0,0,0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glEnable(GL_LIGHTING);

	Camera* pCamera = pNode->GetActiveCamera();
	if(!pCamera)
		return;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(pCamera->m_dfovy,pCamera->m_aspectRatio, pCamera->m_dNear, pCamera->m_dFar );	
		
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(pCamera->m_vPosition.x, pCamera->m_vPosition.y, pCamera->m_vPosition.z, 
			  pCamera->m_vFocusPoint.x, pCamera->m_vFocusPoint.y, pCamera->m_vFocusPoint.z,
			  pCamera->m_upDir.x, pCamera->m_upDir.y, pCamera->m_upDir.z );

	
	//traverse all solid nodes  
	TraverseNodeTree(pNode);

	//Traverse transparent nodes;
	TraverseNodeTree(pNode);
}

void oglRenderer::TraverseNodeTree( SceneNode* pNode )
{
	if(!pNode)
		return; 

	glPushMatrix();	
	glMultTransposeMatrixd( pNode->GetTransformation().GetMatrix() );
	{
		for(int i=0;i<pNode->GetSceneObjectCount();++i)
			DrawSceneObject(pNode->GetSceneObject(i));
	}	
	glPopMatrix();

	for(int i=0;i< pNode->GetChildNodeCount(); ++i)
	{		
		TraverseNodeTree( pNode->GetChild(i) );		
	}
}

void oglRenderer::Initialize()
{
	if(m_pMainContex.valid())
		m_pMainContex->makeCurrent();
	else 
		return;
	//init texture units;
	GLint nCntTexUnit;
	glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&nCntTexUnit);
	for(int i=0;i<nCntTexUnit;++i)
		m_vTexUnits.push_back(new oglTextureUnit(GL_TEXTURE0_ARB+i) );
	
}

void oglRenderer::DrawSceneObject( SceneObject* pObject )
{
	if( NULL == pObject)
		return;

	//apply color
	glColor3fv(pObject->GetColor());

	//apply material
	if(pObject->GetMaterialFront())
		ApplyMaterial(*pObject->GetMaterialFront(),GL_FRONT);

	if(pObject->GetMaterialBack())
		ApplyMaterial(*pObject->GetMaterialBack(),GL_BACK);

	//set texture channel
	const int nTexChCnt = pObject->GetTexChannelCount();
	for(int i=0;i< nTexChCnt;++i)
	{
		TextureChannel& theChannel = pObject->m_vTextureChannels[i];
		if(i < (int)m_vTexUnits.size())
			m_vTexUnits[i]->SetTexture((oglTexture*)theChannel.pTexture);
	}

	//render triangles
	if(pObject->TryFix())
	{
		Point3Array& vertexArray = pObject->m_vVertexs;
		Vector3IndexArray& faceArray = pObject->m_vFaces;

		glBegin(GL_TRIANGLES);
		if(pObject->GetNormMode()== SceneObject::NormPerFace)
		{
			Vector3Array& faceNormArray = pObject->m_vNormPerFace;
			for(int i=0;i< (int)faceArray.size();++i)
			{
				Vector3Index& face=faceArray[i];
				glNormal3dv(faceNormArray[i]);

				
				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,0));
				glVertex3dv(vertexArray[face[0]]);

				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,1));
				glVertex3dv(vertexArray[face[1]]);

				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,2));
				glVertex3dv(vertexArray[face[2]]);
			}
		}
		if( pObject->GetNormMode() == SceneObject::NormPerVertex)
		{
			Vector3Array& vertexNormArray = pObject->m_vNormPerVertex;
			for(int i=0;i< (int)faceArray.size();++i)
			{
				Vector3Index& face=faceArray[i];  

				glNormal3dv(vertexNormArray[face[0]]);
				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,0));
				glVertex3dv(vertexArray[face[0]]);

				glNormal3dv(vertexNormArray[face[1]]);
				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,1));
				glVertex3dv(vertexArray[face[1]]);

				glNormal3dv(vertexNormArray[face[2]]);
				if(nTexChCnt) 
					glTexCoord2dv(pObject->m_vTextureChannels[0].GetFaceVertexCoord(i,2));
				glVertex3dv(vertexArray[face[2]]);
			}
		}
		glEnd();

	}
	
}


void oglRenderer::ApplyMaterial( const Material& pmat, GLenum side )
{		
	glDisable(GL_COLOR_MATERIAL);

	GLfloat shiness = pmat.m_shiness * 128.0f;
	glMaterialfv(side,GL_AMBIENT, pmat.m_ambient);
	glMaterialfv(side,GL_DIFFUSE, pmat.m_diffuse);
	glMaterialfv(side,GL_SPECULAR, pmat.m_sepcular);
	glMaterialfv(side,GL_SHININESS, &shiness);
	glMaterialfv(side,GL_EMISSION, pmat.m_emission);
}

